const axios = require("axios");
const fs = require("fs");
const https = require("https");
const uuid = require("uuid");
const sharp = require("sharp");

const userAgents = [
  "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/125.0.6422.76 Safari/537.36",
  "Mozilla/5.0 (Macintosh; Intel Mac OS X 14_2) AppleWebKit/605.1.15 (KHTML, like Gecko) Version/17.1 Safari/605.1.15",
  "Mozilla/5.0 (iPhone; CPU iPhone OS 17_3 like Mac OS X) AppleWebKit/605.1.15 (KHTML, like Gecko) Mobile/15E148",
  "Mozilla/5.0 (Linux; Android 14; SM-S918B) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/125.0.6422.69 Mobile Safari/537.36",
  "Mozilla/5.0 (X11; Fedora; Linux x86_64; rv:110.0) Gecko/20100101 Firefox/110.0",
  "Mozilla/5.0 (Windows NT 6.1; WOW64; Trident/7.0; AS; rv:11.0) like Gecko",
  "Mozilla/5.0 (iPad; CPU OS 17_2 like Mac OS X) AppleWebKit/605.1.15 (KHTML, like Gecko) Version/17.0 Mobile/15E148 Safari/604.1",
  "Opera/9.80 (Android 14; SM-A546B) Presto/2.12.456 Version/12.18",
  "Mozilla/5.0 (Linux; Android 11; Pixel 6) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/125.0.6422.78 Mobile Safari/537.36 EdgA/116.0.1938.0",
  "Mozilla/5.0 (PlayStation 5; PlayStation 5/OS 11.0.0) AppleWebKit/605.1.15 (KHTML, like Gecko) Version/16.0 Safari/605.1.15",
];

const getRandomUserAgent = () => {
  return userAgents[Math.floor(Math.random() * userAgents.length)];
};

const stringtifyUrl = (url, params) => {
  const search = Object.entries(params)
    .map(([key, value]) => {
      return `${key}=${encodeURIComponent(value)}`;
    })
    .join("&");
  return [url, search].join("?");
};

async function fetchImageUrl(userAgent, word, page = 1) {
  const url = stringtifyUrl("https://image.baidu.com/search/acjson", {
    tn: "resultjson_com",
    word,
    pn: (page - 1) * 30,
    imgratio: 4,
    hd: 1,
  });
  const { data } = await axios.get(url, {
    headers: {
      "User-Agent": userAgent,
    },
  });
  const { data: rets } = data || {};
  if (!Array.isArray(rets)) return [];
  return rets.reduce((rets, item) => {
    const { thumbURL, replaceUrl } = item || {};
    const [replaceUrlItem] = replaceUrl || [];
    const { ObjURL } = replaceUrlItem || {};

    if (thumbURL && ObjURL) {
      return [...rets, { thumbURL, ObjURL }];
    }
    return rets;
  }, []);
}

async function fetchImageUrls(word, userAgents) {
  const images = [];
  for await (const [index, userAgent] of userAgents.entries()) {
    const _images = await fetchImageUrl(userAgent, word, index + 1);
    const delay = Math.floor(Math.random() * 3 + 1) * 1000;
    console.log("图片地址获取间隔时间: ", delay);
    await new Promise((resolve) => setTimeout(resolve, delay));
    images.push(..._images);
  }
  return images;
}

// fetchImageUrls("赵丽颖", userAgents).then((data) => {
//   console.log("data: ", data);
//   console.log("data: ", data.length);
// });

async function fetchImage(url, saveDir) {
  const { data } = await axios
    .get(url, {
      responseType: "stream",
      headers: {
        "User-Agent": userAgents[Math.floor(Math.random() * userAgents.length)],
      },
    })
    .catch(() => {
      return Promise.reject(new Error("request image is fail!"));
    });
  const { headers } = data || {};
  const length = Number(headers?.["content-length"] || "");
  if (length / 1024 < 100) {
    return Promise.reject(new Error("image length is samll or not found!"));
  }
  const [fileType = ""] = (headers?.["content-type"] || "").split(";");
  const [_, imageType] = (fileType || "").split("/");
  if (imageType !== "jpeg") {
    return Promise.reject(new Error("image length is bad or not found!"));
  }

  const file = `${saveDir}/${uuid.v4(12)}.${imageType}`;
  const writer = fs.createWriteStream(file);
  data.pipe(writer);
  await new Promise((resolve) => writer.on("finish", resolve));
  return sharp(file)
    .metadata()
    .catch(() => Promise.reject(new Error("image source is fail!")));
}

async function fetchImages(word, userAgents, normal, hd) {
  const imageUrls = await fetchImageUrls(word, userAgents);
  console.log("待下载图片数量: ", imageUrls.length);

  const poll = async (images, len, dir) => {
    let ans = 0;
    const pools = new Set();
    const _fetchImage = async (image, dir) => {
      const delay = Math.floor(Math.random() * 3 + 1) * 1000;
      await new Promise((resolve) => setTimeout(resolve, delay));
      return fetchImage(image, dir)
        .then((metadata) => {
          // console.log("download success!", `width:${metadata.width}`);
          return Promise.resolve(metadata);
        })
        .catch((error) => {
          console.log("download fail!", error.toString());
        });
    };
    for (let n = 0; n < images.length; n++) {
      const promise = _fetchImage(images[n], dir)
        .then(() => {
          ans++;
        })
        .finally(() => {
          pools.delete(promise);
        });
      pools.add(promise);
      if (pools.size >= len) {
        await Promise.race(pools);
      }
    }
    await Promise.all(pools);
    return ans;
  };

  const _fetchImage_ = async (type, url) => {
    const images = imageUrls.map((item) => item[url]);
    const dir = `D:/图库/${type}/${word}`;
    if (fs.existsSync(dir)) {
      fs.readdirSync(dir).forEach((file) => {
        fs.unlinkSync(`${dir}/${file}`);
      });
      fs.rmdirSync(dir);
    }
    fs.mkdirSync(dir, { recursive: true });
    const ans = await poll(images, 5, dir);
    console.log(`${type}图片数量: `, ans);
  };

  if (normal) {
    await _fetchImage_("普通", "thumbURL");
  }

  if (hd) {
    await _fetchImage_("高清", "ObjURL");
  }
  console.log("下载完毕！");
  process.exit(0);
}

async function fetchContentsImages(contents) {
  for await (const content of contents) {
    console.log("content: ", content);
    await fetchImages(
      content,
      [...userAgents, ...userAgents, ...userAgents, ...userAgents].slice(0, 2),
      false,
      true
    );
  }
}

const contents = [
  "周秀娜",
  "卫诗雅",
  "白冰",
  "倪妮",
  "白鹿",
  "赵丽颖",
  "杨蓉",
  "刘亦菲",
  "周冬雨",
  "赵今麦",
  "李庚希",
  "张雪迎",
  "关晓彤",
  "赵思露",
]
  .slice(0, 1)
  .map((item) => `${item}超清电脑壁纸`);
fetchContentsImages(contents);
